
#ifndef CONNECTOROPENSSL_H
#define CONNECTOROPENSSL_H

#include "RtReactorInterface.h"
#include "RtSocket.h"
#include "RtTransportTcp.h"
#include "RtTransportOpenSsl.h"
#include "RtConnectionInterface.h"
#include "RtConnectorProxyT.h"
//#include "RtTimerWrapper.h"

#ifndef RT_MMP
#include <openssl/ssl.h>
#include <openssl/err.h>
#else
#include "rtssl.h"
#endif

template <class UpperType>
class CRtConnectorOpenSslT 
	: public IRtEventHandler
	, public IRtConnectorInternal
//	, public CRtTimerWrapperSink  //for proxy priority
{
public:
	typedef CRtConnectorOpenSslT SelfType;
	typedef CRtSocketStream SockType;
	typedef CRtTransportOpenSsl TrptType;
	
	CRtConnectorOpenSslT(
			IRtReactor *aReactor, 
			UpperType &aUpper, 
			CRtConnectionManager::CType aType)
		: m_pReactor(aReactor)
		, m_Upper(aUpper)
		, m_ConnectorTcp(aReactor, *this)
		, m_ConnectorProxy(aReactor, *this, TRUE)
		, m_Type(aType)
		, m_TypeOrigin(aType)
//		, m_bConnSucceed(FALSE)
	{
	}

	virtual ~CRtConnectorOpenSslT()
	{
		Close();
	}

	// interface IRtConnectorInternal
	virtual int Connect(const CRtInetAddr &aAddr, CRtInetAddr *aAddrLocal = NULL)
	{
		CRtInetAddr addrSsl(aAddr);
		addrSsl.SetPort(443);
		m_Type = m_TypeOrigin;
		
/*		if (RT_BIT_ENABLED(m_Type, CRtConnectionManager::CTYPE_SSL_WITH_BROWER_PROXY)) {
			m_ConnectTimer.Schedule(this, (LONG)30, 1);
			int nRet = m_ConnectorProxy.Connect(addrSsl, aAddrLocal);
			if (nRet == -1) {
				m_ConnectorProxy.Close();
				RT_CLR_BITS(m_Type, CRtConnectionManager::CTYPE_SSL_WITH_BROWER_PROXY);
			}
		}
*/ //for proxy higher priority

#ifdef RT_MMP
		if (CRtHttpProxyManager::Instance()->IsBrowerProxySet()) {
			RT_WARNING_TRACE_THIS("CRtConnectorOpenSslT::Connect, disable SSL direct.");
			RT_CLR_BITS(m_Type, CRtConnectionManager::CTYPE_SSL_DIRECT);
			if (RT_BIT_DISABLED(m_Type, CRtConnectionManager::CTYPE_SSL_WITH_BROWER_PROXY))
				return -1;
		}
#endif // RT_MMP

		if (RT_BIT_ENABLED(m_Type, CRtConnectionManager::CTYPE_SSL_DIRECT)) {
			int nRet = m_ConnectorTcp.Connect(addrSsl, aAddrLocal);
			if (nRet == -1) {
				m_ConnectorTcp.Close();
				RT_CLR_BITS(m_Type, CRtConnectionManager::CTYPE_SSL_DIRECT);
			}
		}
		if (RT_BIT_ENABLED(m_Type, CRtConnectionManager::CTYPE_SSL_WITH_BROWER_PROXY)) {
			int nRet = m_ConnectorProxy.Connect(addrSsl, aAddrLocal);
			if (nRet == -1) {
				m_ConnectorProxy.Close();
				RT_CLR_BITS(m_Type, CRtConnectionManager::CTYPE_SSL_WITH_BROWER_PROXY);
			}
		}

		if (RT_BIT_DISABLED(m_Type, CRtConnectionManager::CTYPE_SSL_DIRECT) &&
			RT_BIT_DISABLED(m_Type, CRtConnectionManager::CTYPE_SSL_WITH_BROWER_PROXY)) 
		{
			RT_ERROR_TRACE("CRtConnectorOpenSslT::Connect, wrong type=" << m_Type);
			return -1;
		}

		return 0;
	}

	virtual int Close()
	{
		m_ConnectorTcp.Close();
		m_ConnectorProxy.Close();
		if (m_pTransport) {
			// Needn't RemoveHandler here because Transport will do it.
		//	m_pReactor->RemoveHandler(this);
			m_pTransport->Disconnect(RT_OK);
			m_pTransport = NULL;
		}
		return 0;
	}
	
	int OnConnectIndication(
		RtResult aReason, 
		TrptType *aTrpt,
		IRtConnectorInternal *aId)
	{
		int nRet;
		RtResult rv;
		SSL *pSsl;
		RT_INFO_TRACE_THIS("CRtConnectorOpenSslT::OnConnectIndication,"
			" aReason=" << aReason << 
			" aTrpt=" << aTrpt << 
			" aId=" << aId);
		
		if (RT_FAILED(aReason)) {
			RT_ASSERTE(!aTrpt);

			if (&m_ConnectorTcp == aId) {
				RT_ASSERTE(RT_BIT_ENABLED(m_Type, CRtConnectionManager::CTYPE_SSL_DIRECT));
				RT_CLR_BITS(m_Type, CRtConnectionManager::CTYPE_SSL_DIRECT);
				m_ConnectorTcp.Close();
			}
			else if (&m_ConnectorProxy == aId) {
				RT_ASSERTE(RT_BIT_ENABLED(m_Type, CRtConnectionManager::CTYPE_SSL_WITH_BROWER_PROXY));
				RT_CLR_BITS(m_Type, CRtConnectionManager::CTYPE_SSL_WITH_BROWER_PROXY);
				m_ConnectorProxy.Close();
			}

			if (RT_BIT_DISABLED(m_Type, CRtConnectionManager::CTYPE_SSL_DIRECT) &&
				RT_BIT_DISABLED(m_Type, CRtConnectionManager::CTYPE_SSL_WITH_BROWER_PROXY)) 
			{
				goto fail;
			}

			// wait for another connector.
			return 0;
		}

		if (&m_ConnectorTcp == aId) {
			RT_ASSERTE(RT_BIT_ENABLED(m_Type, CRtConnectionManager::CTYPE_SSL_DIRECT));
//			if(!RT_BIT_ENABLED(m_Type, CRtConnectionManager::CTYPE_SSL_WITH_BROWER_PROXY))
//			{
			RT_CLR_BITS(m_Type, CRtConnectionManager::CTYPE_SSL_WITH_BROWER_PROXY);
			m_ConnectorProxy.Close();
//				m_bConnSucceed = TRUE;
//			}
		}
		else if (&m_ConnectorProxy == aId) {
			RT_ASSERTE(RT_BIT_ENABLED(m_Type, CRtConnectionManager::CTYPE_SSL_WITH_BROWER_PROXY));
			RT_CLR_BITS(m_Type, CRtConnectionManager::CTYPE_SSL_DIRECT);
			m_ConnectorTcp.Close();
/*			m_bConnSucceed = TRUE;
			if(m_pTransport) //directly connection already built?
				m_pTransport->Disconnect(RT_OK);
			m_ConnectTimer.Cancel();
*/
		}

		m_pTransport = aTrpt;
//		if(m_bConnSucceed) ///connect succeed, if proxy enable and not back, wait it in timer
//		{
		RT_ASSERTE(m_pTransport);

		if (m_pReactor->GetProperty() & IRtReactor::SEND_REGISTER_PROPERTY)
			rv = m_pReactor->RegisterHandler(this, IRtEventHandler::READ_MASK);
		else 
			rv = m_pReactor->RegisterHandler(this, IRtEventHandler::READ_MASK | IRtEventHandler::WRITE_MASK);
		if (RT_FAILED(rv) && rv != RT_ERROR_FOUND) {
			RT_ERROR_TRACE_THIS("CRtConnectorOpenSslT::OnConnectIndication,"
				" RegisterHandler(READ_MASK|WRITE_MASK) failed!");
			goto fail;
		}

		nRet = m_pTransport->InitSsl(m_Type);
		if (nRet == -1)
			goto fail;

		pSsl = m_pTransport->GetSslPtr();
		RT_ASSERTE(pSsl);
#ifdef RT_MMP
		CRTSSL::SSL_set_connect_state(pSsl);
#else
		::SSL_set_connect_state(pSsl);
#endif
		nRet = DoHandshake();
		if (nRet == -1)
			goto fail;
		else if (nRet == 0) {
			RT_WARNING_TRACE("CRtConnectorOpenSslT::OnConnectIndication,"
				" connnect return 0.");
		}
//		}
		return 0;

fail:
		Close();
		if (RT_SUCCEEDED(aReason))
			aReason = RT_ERROR_NETWORK_SOCKET_ERROR;
		m_Upper.OnConnectIndication(aReason, NULL, this);
		return 0;
	}
/*	virtual void OnTimer(CRtTimerWrapper* aId)
	{
		RT_ASSERTE(aId == &m_ConnectTimer);
		int nRet;
		RtResult rv;
		if(m_pTransport)
		{
			RT_ASSERTE(m_pTransport);
			RT_CLR_BITS(m_Type, CRtConnectionManager::CTYPE_SSL_WITH_BROWER_PROXY);
			m_ConnectorProxy.Close();

	#ifdef RT_USE_REACTOR_SELECT
			rv = m_pReactor->RegisterHandler(this, IRtEventHandler::READ_MASK);
	#else
			rv = m_pReactor->RegisterHandler(this, 
				IRtEventHandler::READ_MASK | IRtEventHandler::WRITE_MASK);
	#endif // RT_USE_REACTOR_SELECT
			if (RT_FAILED(rv) && rv != RT_ERROR_FOUND) {
				RT_ERROR_TRACE_THIS("CRtConnectorOpenSslT::OnConnectIndication,"
					" RegisterHandler(READ_MASK|WRITE_MASK) failed!");
				goto fail;
			}

			nRet = m_pTransport->InitSsl(m_Type);
			if (nRet == -1)
				goto fail;

			SSL *pSsl = m_pTransport->GetSslPtr();
			RT_ASSERTE(pSsl);
	#ifdef RT_MMP
			CRTSSL::SSL_set_connect_state(pSsl);
	#else
			::SSL_set_connect_state(pSsl);
	#endif
			nRet = DoHandshake();
			if (nRet == -1)
				goto fail;
			else if (nRet == 0) {
				RT_WARNING_TRACE("CRtConnectorOpenSslT::OnConnectIndication,"
					" connnect return 0.");
			}
			return;
		}
		RT_INFO_TRACE_THIS("CRtConnectorOpenSslT::OnTimer connect no response");
fail:
		Close();
		m_Upper.OnConnectIndication(RT_ERROR_NETWORK_SOCKET_ERROR, NULL, this);
	}
*/

	virtual RT_HANDLE GetHandle() const 
	{
		RT_ASSERTE_RETURN(m_pTransport, RT_INVALID_HANDLE);
		return m_pTransport->GetHandle();
	}
	
	virtual int OnInput(RT_HANDLE aFd = RT_INVALID_HANDLE)
	{
		RT_ASSERTE_RETURN(m_pTransport, -1);
		int nRet = m_pTransport->RecvFromSocket();
		if (nRet <= 0)
			return nRet;
		
		nRet = DoHandshake();
		if (nRet == -1)
			return nRet;
		else
			return 0;
	}

	virtual int OnOutput(RT_HANDLE aFd = RT_INVALID_HANDLE)
	{
		int nRet = DoHandshake();
		if (nRet == -1)
			return nRet;
		else
			return 0;
	}

	virtual int OnClose(RT_HANDLE aFd, MASK aMask)
	{
		Close();
		m_Upper.OnConnectIndication(RT_ERROR_NETWORK_SOCKET_ERROR, NULL, this);
		return 0;
	}

private:
	int DoHandshake()
	{
		RT_ASSERTE_RETURN(m_pTransport, -1);

		SSL *pSsl = m_pTransport->GetSslPtr();
#ifdef RT_MMP
		int nConn = CRTSSL::SSL_connect(pSsl);
		int nErr = CRTSSL::SSL_get_error(pSsl, nConn);
#else
		int nConn = ::SSL_connect(pSsl);
		int nErr = ::SSL_get_error(pSsl, nConn);
#endif
		switch (nErr) {
		case SSL_ERROR_NONE: {
			CRtAutoPtr<TrptType> pTrans = m_pTransport;
			m_pTransport = NULL;
			m_Upper.OnConnectIndication(RT_OK, pTrans.ParaIn(), this);
			return 0;
							 }

		case SSL_ERROR_WANT_READ:
		case SSL_ERROR_WANT_WRITE:
			return 1;

		default:
			CRtTransportOpenSsl::TraceOpenSslError(
				"CRtConnectorOpenSslT::DoHandshake, SSL_connect() failed!", 
				this);
			return -1;
		}
	}

	IRtReactor *m_pReactor;
	UpperType &m_Upper;
	CRtConnectorTcpT<SelfType, TrptType, SockType> m_ConnectorTcp;
	CRtConnectorProxyT<SelfType, TrptType, SockType> m_ConnectorProxy;
	CRtAutoPtr<TrptType> m_pTransport;
	CRtConnectionManager::CType m_Type;
	CRtConnectionManager::CType m_TypeOrigin;
//	BOOL m_bConnSucceed;
};

#endif // !CONNECTOROPENSSL_H
